#include "sintatic_analiser.h"

void print_lexic_matrix(vector<Chamada> &a) {
	for (int i = 0; i < (int) a.size(); i++) {
		cout << a[i].chamada << " " << a[i].simbolo << " " << a[i].cadeia
				<< " " << a[i].codigo << endl;
	}
}

vector<Chamada> lexic_matrix;

vector<Chamada> get_lexic_matrix(){
	return lexic_matrix;
}


int n_simbolo;

Chamada simbolo;

inline string int_to_string(int a){
	char n[5];
	itoa(a, n, 10);
	return string(n);
}

bool have_simbolo() {
	return (n_simbolo < (int) lexic_matrix.size());
}

void get_simbolo() {
	if (have_simbolo()) {
		simbolo = lexic_matrix[n_simbolo];
	} else {
		simbolo.cadeia = "$";
		simbolo.chamada = n_simbolo;
		simbolo.codigo = CERQUILHA_FINAL;
		simbolo.simbolo = "Simbolo Final";
	}
	cout << "SIMBOLO ATUAL: (" << simbolo.codigo << ") " << simbolo.cadeia << endl;
	n_simbolo++;
}

void init() {
	n_simbolo = 0;
	lexic_matrix = get_lexic_analiser();
}

void sintatic_analiser() {
	try {
		get_simbolo();
		program();
		if (simbolo.codigo != CERQUILHA_FINAL) {
			throw string(int_to_string(simbolo.linha) + string(":erro: Esperado fim do programa."));
		}
	} catch (string str) {
		throw str;
	}
}

void data_declaration_list() {
	if(simbolo.codigo == SIMBOLO_INVALIDO){
		throw string(int_to_string(simbolo.linha) + string(":erro:Simbolo Invalido Encontrado."));
	}
	data_declaration();
	data_declaration_list2();
}

void data_declaration_list2() {
	if (simbolo.codigo == NOME_DE_TIPO) {
		data_declaration_list();
	}
}

void data_declaration() {

	if(simbolo.codigo == CERQUILHA_FINAL ) return;

	type();
	identifier_list();
	if (simbolo.codigo != PONTO_E_VIRGULA) {
		throw string(int_to_string(simbolo.linha) + string(":erro:Esperado ';'."));
	} else {
		get_simbolo();
	}
}

void identifier_list() {
	value();
	identifier_list2();
}

void identifier_list2() {
	// , <identifier-list> | vazio
	if (simbolo.codigo == VIRGULA) {
		get_simbolo();
		identifier_list();
	}
}

void value() {
	if (simbolo.codigo != NUMERO && simbolo.codigo != STRING) {
		identifier();
		value2();
	} else if (simbolo.codigo == NUMERO) {
		/* Trata numero */
		get_simbolo();
	} else if (simbolo.codigo == STRING) {
		/* Trata string */
		get_simbolo();
	} else {
		throw string(int_to_string(simbolo.linha) + string(":erro: Esperado valor ou constante."));
	}
}

void value2() {
	if (simbolo.codigo == ABRE_COLCHETE) {
		get_simbolo();
		expression();
		if (simbolo.codigo != FECHA_COLCHETE) {
			throw string(int_to_string(simbolo.linha) + string(":erro: Esperado ']'."));
		} else {
			get_simbolo();
		}
		value2();
	}
}

void expression() {
	// <logical-term><comparison-term>
	logical_term();
	comparison_term();
}

void primary() {
	// <value> | ( <expression> )
	if (simbolo.codigo == ABRE_PARENTESE) {
		get_simbolo();
		expression();
		if (simbolo.codigo != FECHA_PARENTESE) {
			throw int_to_string(simbolo.linha) + string(":erro: Esperado ).");
		} else {
			get_simbolo();
		}
	} else {
		value();
	}
}

void multiplication_division_operator() {
	if (simbolo.codigo == MULTIPLICACAO || simbolo.codigo == DIVISAO) {
		/* Trata sinal */
		get_simbolo();
	} else {
		throw int_to_string(simbolo.linha) + string(":error:Esperado sinal * ou /.");
	}
}

void high_operation() {
	// <multiplication-division-mod><primary><high-operation> | vazio
	if (simbolo.codigo == MULTIPLICACAO || simbolo.codigo == DIVISAO) {
		multiplication_division_operator();
		primary();
		high_operation();
	}
}

void high_precedence() {
	// <primary><high-operation>
	primary();
	high_operation();
}

void arithm_operation() {
	// <sum-subtraction-operator><high-precedence><arithm-operation> | vazio
	if (simbolo.codigo == SOMA || simbolo.codigo == SUBTRACAO || simbolo.codigo
			== CONCATENACAO) {
		sum_subtraction_operator();
		high_precedence();
		arithm_operation();
	}
}

void sum_subtraction_operator() {
	if (simbolo.codigo == SOMA || simbolo.codigo == SUBTRACAO || simbolo.codigo
			== CONCATENACAO) {
		/* Trata sinal */
		get_simbolo();
	} else {
		throw int_to_string(simbolo.linha) + string(":erro: Esperado sinal +, - ou @.");
	}
}

void arithm_term() {
	// <high-precedence><arithm-operation>
	high_precedence();
	arithm_operation();
}

void logical_operation() {
	// <and-or><arith-term><logical-operation> | vazio
	if (simbolo.codigo == OU_BOOLEANO || simbolo.codigo == E_BOOLEANO) {
		and_or();
		arithm_term();
		logical_operation();
	}
}

void and_or() {
	if (simbolo.codigo == E_BOOLEANO || simbolo.codigo == OU_BOOLEANO) {
		/* Trata booleano */
		get_simbolo();
	} else {
		throw int_to_string(simbolo.linha) + string(":error: Esperado Operador Booleano.");
	}
}

void comparator() {
	// < | > | <= | >= | == | !=
	if (simbolo.codigo == MENOR || simbolo.codigo == MAIOR || simbolo.codigo
			== MENOR_IGUAL || simbolo.codigo == MAIOR_IGUAL || simbolo.codigo
			== IGUALDADE || simbolo.codigo == DIFERENCA) {
		/* Trata comparador. */
		get_simbolo();
	} else {
		throw int_to_string(simbolo.linha) + string("erro: Esperado comparador.");
	}
}

void comparison_term() {
	// <comparator><logical-term><comparison-term> | vazio
	if (
		simbolo.codigo == MAIOR || 
		simbolo.codigo == MENOR || 
		simbolo.codigo == MAIOR_IGUAL || 
		simbolo.codigo == MENOR_IGUAL || 
		simbolo.codigo == IGUALDADE || 
		simbolo.codigo == DIFERENCA
	) {
		comparator();
		logical_term();
		comparison_term();
	}
}

void type() {
	if (simbolo.codigo == NOME_DE_TIPO ){
		get_simbolo();
	} else {
		throw string(int_to_string(simbolo.linha) + "erro:esperado void ou tipo.");
		get_simbolo();
	}
}

void logical_term() {
	// <arithm-term><logical-operation>
	arithm_term();
	logical_operation();
}

void function_definition_list() {
	function_definition();
	function_definition_list2();
}

void function_call(){

	// <alpha-numeric-name>()

	if (simbolo.codigo == IDENTIFICADOR_DE_FUNCAO) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado nome da funcao.");
	
	if (simbolo.codigo == ABRE_PARENTESE) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado '(' da funcao.");

	if (simbolo.codigo == FECHA_PARENTESE) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado ')' da funcao.");

}

void function_identifier() {

	// function <tipo> <alpha-numeric-name>()

	if( simbolo.codigo == DECLARACAO_FUNCAO ) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado 'function'");

	if( simbolo.codigo == NOME_DE_TIPO ) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado void ou tipo da funcao");

	if (simbolo.codigo == IDENTIFICADOR_DE_FUNCAO) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado nome da funcao.");
	
	if (simbolo.codigo == ABRE_PARENTESE) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado '(' da funcao.");

	if (simbolo.codigo == FECHA_PARENTESE) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado ')' da funcao.");

}

void statement_list() {
	// <statement>; <statement-list'>
	statement();
	statement_list2();
}

void statement_list2() {
	if (simbolo.codigo != FECHA_CHAVE) {
		statement_list();
	}
}

void statement() {

	if (simbolo.codigo != FECHA_CHAVE) {
		//		<value> = <expression>; | <function_call> | { <statement-list> }
		//		| if( <expression> ) <statement> else <statement>
		//		| while ( <expression> ) <statement>;
		//		| print <expression>; | echo <expression>; | read <identifier>;
		if (simbolo.codigo == IDENTIFICADOR) {

			value();
			if (simbolo.codigo == ATRIBUICAO) get_simbolo();
			else throw int_to_string(simbolo.linha) + string(":erro: Esperado '='.");
			expression();

		} else if (simbolo.codigo == IDENTIFICADOR_DE_FUNCAO) {
			function_call();
		} else if (simbolo.codigo == ABRE_CHAVE) {
			get_simbolo();
			statement_list();
			if (simbolo.codigo != FECHA_CHAVE) {
				throw int_to_string(simbolo.linha) + string(":erro: Esperado '}'.");
			} else {
				get_simbolo();
			}
		} else if (simbolo.cadeia == "if") {
			get_simbolo();
			if (simbolo.codigo != ABRE_PARENTESE) {
				throw int_to_string(simbolo.linha) + string(":erro: Esperado (.");
			} else {
				get_simbolo();
			}
			expression();
			if (simbolo.codigo != FECHA_PARENTESE) {
				throw int_to_string(simbolo.linha) + string(":erro: Esperado ).");
			} else {
				get_simbolo();
			}
			statement();
			if (simbolo.cadeia != "else") {
				throw int_to_string(simbolo.linha) + string(":erro: Esperado else.");
			} else {
				get_simbolo();
			}
			statement();
		} else if (simbolo.cadeia == "while") {

			get_simbolo();

			if (simbolo.codigo == ABRE_PARENTESE) get_simbolo();
			else throw int_to_string(simbolo.linha) + string(":erro: Esperado (.");
			
			expression();

			if (simbolo.codigo == FECHA_PARENTESE) get_simbolo();
			else throw int_to_string(simbolo.linha) + string("Error 14: Esperado ).");

			statement();

		} else if (simbolo.cadeia == "print") {
			get_simbolo();
			expression();
		} else if (simbolo.cadeia == "echo") {
			get_simbolo();
			expression();
		} else if (simbolo.cadeia == "read") {
			get_simbolo();
			identifier();
		}else if (simbolo.cadeia == "return") {
			get_simbolo();
			expression();
		}else if (simbolo.codigo == PONTO_E_VIRGULA) {
			get_simbolo();
		}else{
			throw int_to_string(simbolo.linha) + string(":erro: Esperado ';'.");
	}
	}

}

void function_definition() {

	if( simbolo.codigo == CERQUILHA_FINAL ) return;

	//	function <type> <function_identifier> { <statement-list> }
	function_identifier();

	if (simbolo.codigo == ABRE_CHAVE) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado {.");

	statement_list();

	if (simbolo.codigo == FECHA_CHAVE) get_simbolo();
	else throw int_to_string(simbolo.linha) + string(":erro: Esperado }.");

}

void function_definition_list2() {
	if (simbolo.codigo == DECLARACAO_FUNCAO ) {
		function_definition_list();
	}
}

void data_definition() {

	if (simbolo.codigo != DECLARACAO_FUNCAO ) {
		data_declaration_list();
	}
}

void program() {
	data_definition();
	function_definition_list();
}


void identifier() {
	if (simbolo.codigo == IDENTIFICADOR) {
		/* Trata Identificador de variavel */
		get_simbolo();
	} else if (simbolo.codigo == IDENTIFICADOR_DE_FUNCAO) {
		/* Trata Identificador de funcao */
		function_call();
	} else if(simbolo.codigo == SIMBOLO_INVALIDO){
		throw string(int_to_string(simbolo.linha) + string(":erro:Simbolo Invalido Encontrado."));
	}else {
		throw string(int_to_string(simbolo.linha) + string(":erro:Esperado Identificador de funcao ou variavel."));
	}
}

//int main(int arg_count, char *args[] ) {
//
//	try {
//		init();
//		print_lexic_matrix(lexic_matrix);
//		sintatic_analiser();
//	} catch (string str) {
//		cout << str << endl;
//	}
//
//	return (0);
//}
